// 引入头文件
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
// #include <winsock.h>
// #include <winsock2.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
// #pragma comment(lib, "wsock32.lib")
# include<signal.h>
#define END "END\n"  // 结束标志

// 定义宏
#define MAXLINE 1024 // 缓冲区大小
#define TRUE 1
#define END "END\n"  // 结束标志
#define USER "TOM"   // 定义Server用户名


int main(int argc, char **argv)
{
    int sockfd, fd, n, m; // 这里的sockfd是网络连接的套接字文件描述符，用于标记套接字接口
    char rec_line[MAXLINE + 1] , sen_line[MAXLINE + 1]; // 定义用于接受和发送数据的缓冲区
    struct sockaddr_in6 servaddr, cliaddr; // 定义服务器地址和客户端地址
    time_t t0 = time(NULL); // 定义时间变量，表示从Unix时间戳开始的时间
    fputs(ctime(&t0), stdout); // 使用ctime函数格式化打印出当前的本地时间
    printf("your user name is %s \n",USER);

    // 创建套接字，并判断是否创建失败
    if ((sockfd = socket(AF_INET6, SOCK_STREAM, 0)) < 0)
        // 当套接字文件描述符为负数的时候，创建失败
        perror("socket error"); // perror用于输出系统错误

    bzero(&servaddr, sizeof(servaddr)); // 将服务器地址所在的内存均写为0
    servaddr.sin6_family = AF_INET6; // 设置套接字地址类型为AF_INET6，即只接受IPv6地址
    servaddr.sin6_port = htons(20000); // 监听20000端口，其中htons把电脑上数据存储顺序的无符号短整型数转换成网络上的数据存储顺序的短整型数
    servaddr.sin6_addr = in6addr_any; // 服务器IPv6地址，这里in6addr_any表示将套接字绑定在所有网络接口上

    // 将套接字绑定到指定地址上，并判断是否绑定失败
    if (bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1)
        perror("bind error");

    // 标记套接字为被动套接字，以便之后通过accept函数获取连接请求
    if (listen(sockfd, 5) == -1)
        perror("listen error");
    
    printf("> Waiting clients ...\r\n");
    socklen_t clilen = sizeof(struct sockaddr); // 定义socklen_t类变量clilen用来记录sockaddr结构体的长度
    fd = accept(sockfd, (struct sockaddr *)&cliaddr, &clilen); // 使套接字开始接收客户端的请求
    if (fd == -1)
        // 当文件描述符为-1时，请求接收失败
        perror("accept error");

    char user[100]={};
    char send_message[MAXLINE+100]={};
    strcpy(user,USER);
    strcat(user,": ");

    // 创建子进程
    pid_t pid;
    pid = fork(); // 得到进程标识符
    if(pid <0)
        perror("fork error");
    if(pid == 0){ //子进程中用来向Client端发送数据
        while(fgets(sen_line, MAXLINE, stdin) != NULL){
            if (strcmp(sen_line,END)!=0){
                strcat(send_message,user);  // 在发送数据的前端加上用户名
                strcat(send_message,sen_line);
                n = write(fd, send_message, strlen(send_message)); //将发送缓冲区的数据发送给Client端
            }
            else{
                n = write(fd, sen_line, strlen(sen_line)); //将发送缓冲区的数据发送给Client端
            }
            if(n < 0)
                perror("write error");
            bzero(sen_line,strlen(sen_line));//发送完成清空发送缓冲区
            bzero(send_message,strlen(send_message)); //清空发送信息  
        }
        exit(EXIT_SUCCESS);//退出子进程
    }
    else{
        while(TRUE){
            bzero(rec_line,strlen(rec_line));
            n = read(fd, rec_line, sizeof(rec_line));// 从接收到的文件描述符中把数据读取到缓冲区line中，n为读取到的数据的长度
            if(n ==-1)
                perror("read error");
            else if(n == 0){
                printf("client is close!\n");
                break;//父进程收到服务器端退出的信息（服务器Ctrl+C结束通信进程，read函数返回值为0，退出循环）
            }
            if(strcmp(rec_line,END)==0){
                printf("receive signal to close chat.See you!\n");
                break;
            }
            rec_line[n] = 0; // 将读取到的数据的下一位写0，便于输出
            if (fputs(rec_line, stdout) == EOF)
                // 输出失败，引起错误
                perror("fputs error");
        }
        kill(pid,SIGUSR1);// 父进程结束，也要向子进程发出一个信号告诉子进程终止接收，否则子进程会一直等待输入
    }
    close(fd);
    close(sockfd);

    // 退出程序
    exit(0);
}